package com.kos.backend.consumer.utils;

import com.alibaba.fastjson2.JSONObject;
import com.kos.backend.consumer.WebSocketServer;
import com.kos.backend.mapper.UserMapper;
import com.kos.backend.pojo.Record;
import com.kos.backend.pojo.Snake;
import com.kos.backend.pojo.User;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Random;
import java.util.concurrent.locks.ReentrantLock;

public class Game extends Thread{
    private final Integer rows;
    private final Integer cols;
    private final Integer inner_walls_count;
    private final int[][] g;
    private static final int[] dx = {-1,0,1,0};
    private static final int[] dy = {0,1,0,-1};
    private final Player playerA;
    private final Player playerB;
    private Integer nextStepA;
    private Integer nextStepB;
    private final ReentrantLock reentrantLock = new ReentrantLock();
    private String status = "playing"; //playing -> finished
    private String loser = ""; //All 平局  A B

    private final static String addSnakeUrl = "http://127.0.0.1:8092/snake/add";

    public Game(Integer rows, Integer cols, Integer inner_walls_count, Integer idA, Snake snakeA , Integer idB, Snake snakeB){
        this.rows = rows;
        this.cols = cols;
        this.inner_walls_count = inner_walls_count;
        this.g = new int[rows][cols];

        Integer snakeIdA = -1;
        Integer snakeIdB = -1;
        String snakeCodeA = "";
        String snakeCodeB = "";
        if(snakeA != null){
            snakeIdA = snakeA.getId();
            snakeCodeA = snakeA.getContent();
        }
        if(snakeB != null){
            snakeIdB = snakeB.getId();
            snakeCodeB = snakeB.getContent();
        }
        playerA = new Player(idA,snakeIdA,snakeCodeA,rows - 2,1,new ArrayList<>());
        playerB = new Player(idB,snakeIdB,snakeCodeB,1,cols - 2,new ArrayList<>());
    }

    public int[][] getG(){
        return g;
    }

    public Player getPlayerA(){
        return this.playerA;
    }
    public Player getPlayerB(){
        return this.playerB;
    }

    public void setNextStepA(Integer nextStepA){
        reentrantLock.lock();
        //避免死锁
        try {
            this.nextStepA = nextStepA;
        } finally {
            reentrantLock.unlock();
        }
    }
    public void setNextStepB(Integer nextStepB){
        reentrantLock.lock();
        try{
            this.nextStepB = nextStepB;
        } finally {
            reentrantLock.unlock();
        }
    }

    private boolean check_connectivity(int sx,int sy,int tx,int ty){
        if(sx == tx && sy == ty){
            return true;
        }
        g[sx][sy] = 1;

        for(int i = 0; i < 4; i ++){
            int x = sx + dx[i];
            int y = sy + dy[i];
            if(x >= 0 && x < this.rows && y >= 0 && y < this.cols && g[x][y] == 0){
                if(check_connectivity(x,y,tx,ty)){
                    g[sx][sy] = 0;
                    return true;
                }
            }
        }
        g[sx][sy] = 0;
        return false;
    }

    //画地图 0表示地图，1表示墙
    private boolean draw(){
        for(int i = 0; i < this.rows; i ++){
            for(int j = 0; j < this.cols; j ++){
                g[i][j] = 0;
            }
        }
        for(int r = 0; r < this.rows; r ++){
            g[r][0] = g[r][this.cols - 1] = 1;
        }
        for(int c = 0; c < this.cols; c ++){
            g[0][c] = g[this.rows - 1][c] = 1;
        }
        Random random = new Random();
        for(int i = 0; i < this.inner_walls_count / 2; i ++){
            for(int j = 0; j < 1000; j ++){
                int r = random.nextInt(this.rows);
                int c = random.nextInt(this.cols);

                if(g[r][c] == 1 || g[this.rows - 1 - r][this.cols - 1 - c] == 1){
                    continue;
                }
                if(r == this.rows - 2 && c == 1 || r == 1 && c == this.cols - 2){
                    continue;
                }
                g[r][c] = g[this.rows - 1 - r][this.cols - 1 - c] = 1;
                break;
            }
        }
        return check_connectivity(this.rows - 2,1,1,this.cols - 2);
    }

    public void creatMap(){
        for(int i = 0; i < 1000; i ++){
            if(draw()){
                break;
            }
        }
    }

    //将当前的局面信息编码为字符串
    private String getInput(Player player){
        Player a , b;
        if(getPlayerA().getId().equals(player.getId())){
            a = playerA;
            b = playerB;
        } else{
            a = playerB;
            b = playerA;
        }
        return getMapString() + "#" + a.getSx() + "#" + a.getSy() + "#(" + a.getStepsString() + ")#" + b.getSx() + "#" + b.getSy() + "#(" + b.getStepsString() + ")";
    }

    private void sendSnakeCode(Player player){
        //本人在玩，不需要ai
        if(player.getSnakeId().equals(-1)){
            return;
        }
        MultiValueMap<String,String> data = new LinkedMultiValueMap<>();
        data.add("user_id",player.getId().toString());
        data.add("snake_code",player.getSnakeCode());
        data.add("input",getInput(player));
        WebSocketServer.restTemplate.postForObject(addSnakeUrl,data,String.class);
    }

    //等待两个玩家的下一步操作
    private boolean nextStep(){
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        sendSnakeCode(playerA);
        sendSnakeCode(playerB);

        for(int i = 0; i < 50; i ++) {
            try {
                Thread.sleep(100);
                reentrantLock.lock();
                try{
                    if(nextStepA != null && nextStepB != null){
                        playerA.getSteps().add(nextStepA);
                        playerB.getSteps().add(nextStepB);
                        return true;
                    }
                } finally {
                    reentrantLock.unlock();
                }
            } catch (InterruptedException e) {
                e.printStackTrace();

            }
        }
        return false;
    }

    private boolean check_valid(List<Cell> cellsA, List<Cell> cellsB){
        int n = cellsA.size();
        Cell cell = cellsA.get(n - 1);
        if(g[cell.x][cell.y] == 1){
            return false;
        }
        for(int i = 0; i < n - 1; i ++){
            if(cellsA.get(i).x == cell.x && cellsA.get(i).y == cell.y){
                return false;
            }
        }
        for(int i = 0; i < n - 1; i ++){
            if(cellsB.get(i).x == cell.x && cellsB.get(i).y == cell.y){
                return false;
            }
        }
        return true;
    }

    //判断两个玩家下一步操作是否合法
    private void judge(){
        List<Cell> cellsA = playerA.getCells();
        List<Cell> cellsB = playerB.getCells();
        boolean validA = check_valid(cellsA,cellsB);
        boolean validB = check_valid(cellsB,cellsA);
        if(! validA || ! validB){
            status = "finished";
            if(!validA && !validB){
                loser = "All";
            } else if(!validA){
                loser = "A";
            } else {
                loser = "B";
            }
        }
    }

    private void sendAllMessage(String message){
        if(WebSocketServer.users.get(playerA.getId()) != null){
            WebSocketServer.users.get(playerA.getId()).sendMessage(message);
        }
        if(WebSocketServer.users.get(playerB.getId()) != null){
            WebSocketServer.users.get(playerB.getId()).sendMessage(message);
        }
    }

    //向两名用户传递移动信息
    private void sendMove(){
        reentrantLock.lock();
        try{
            JSONObject resp = new JSONObject();
            resp.put("event","move");
            resp.put("a_direction",nextStepA);
            resp.put("b_direction",nextStepB);
            sendAllMessage(resp.toJSONString());
            nextStepA = nextStepB = null;
        } finally {
            reentrantLock.unlock();
        }

    }

    //向两个客户端发送结果
    private void sendResult(){
        JSONObject resp = new JSONObject();
        resp.put("event","result");
        resp.put("loser",loser);
        saveToDatabase();
        sendAllMessage(resp.toJSONString());
    }

    private String getMapString(){
        StringBuilder res = new StringBuilder();
        for(int i = 0; i < rows; i ++){
            for(int j = 0; j < cols; j ++){
                res.append(g[i][j]);
            }
        }
        return res.toString();
    }

    private void updateUserRating(Player player, Integer rating){
        User user = WebSocketServer.userMapper.selectById(player.getId());
        user.setRating(rating);
        WebSocketServer.userMapper.updateById(user);
    }

    private void saveToDatabase(){

        Integer ratingA = WebSocketServer.userMapper.selectById(playerA.getId()).getRating();
        Integer ratingB = WebSocketServer.userMapper.selectById(playerB.getId()).getRating();

        if("A".equals(loser)){
            ratingA -= 2;
            ratingB += 5;
        } else if("B".equals(loser)) {
            ratingA += 5;
            ratingB -= 2;
        }

        updateUserRating(playerA,ratingA);
        updateUserRating(playerB,ratingB);

        Record record = new Record(null,playerA.getId(), playerA.getSx(),playerA.getSy(),playerB.getId(),playerB.getSx(),playerB.getSy(),playerA.getStepsString(),playerB.getStepsString(),loser,getMapString(),new Date());
        WebSocketServer.recordMapper.insert(record);
    }


    @Override
    public void run() {
        for(int i = 0; i < 1000; i ++){
            //判断一下是否获取到两条蛇的下一步操作
            if(nextStep()){
                judge();
                if(status.equals("playing")){
                    sendMove();
                } else{
                    sendResult();
                    break;
                }
            } else{
                status = "finished";
                reentrantLock.lock();
                try{
                    if(nextStepA == null && nextStepB == null){
                        loser = "All";
                    } else if(nextStepA == null){
                        loser = "A";
                    } else{
                        loser = "B";
                    }
                } finally {
                    reentrantLock.unlock();
                }
                sendResult();
                break;
            }
        }
        super.run();
    }
}
